home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1122 / 1122.xpi / chrome / tabmixplus.jar / content / tabmixplus / tab / tab.js next >
Text File  |  2009-10-11  |  76KB  |  1,842 lines

  1. // code based on Tab X 0.5 enhanced version by Morac, modified by Hemiola SUN, later CPU & onemen
  2.  
  3. var nsIPrefServiceObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
  4. var tabxBranch = "extensions.tabmix.";
  5. var tabxPrefs = nsIPrefServiceObj.getBranch(tabxBranch);
  6.  
  7. var gTMPprefObserver = null;
  8. var gTabBarWidth = -1;
  9. var addtabx;
  10. var tabxleft;
  11. var tabscroll;
  12. var gWidthFitTitle;
  13. var gTabbarPosition = 0;
  14. var gSingleWindowMode;
  15. var alwaysNewTab;
  16. var gHideTabBar;
  17. var gPreventUpdate;
  18.  
  19. function TMupdateSettings(start) {
  20.   // getBrowser() here for Firefox 3.0
  21.   var tabBrowser = getBrowser();
  22.   if (!tabBrowser || tabxPrefs.prefHasUserValue("setDefault") || gPreventUpdate == true)
  23.     return;
  24.  
  25.   var tabBar  = tabBrowser.mTabContainer;
  26.  
  27.   var i;
  28.   addtabx =          TMP_getIntPref (tabxBranch, "tabXMode",         1);
  29.   // in old version we use tabXMode = 0 to disable the button
  30.   if (addtabx < 1 || addtabx > 5) {
  31.     addtabx = 1;
  32.     tabxPrefs.setIntPref("tabXMode", 1);
  33.     return;
  34.   }
  35.   tabxleft =         TMP_getBoolPref(tabxBranch, "tabXLeft",         false);
  36.   tabscroll =        TMP_getIntPref (tabxBranch, "tabBarMode",       1);
  37.   if (tabscroll < 0 || tabscroll > 3 || (tabscroll != 1 && "TreeStyleTabBrowser" in window)) {
  38.     tabxPrefs.setIntPref("tabBarMode", 1);
  39.     return;
  40.   }
  41.  
  42.   tabBar.mTabMaxWidth  = gTabmixPrefs.getIntPref("browser.tabs.tabMaxWidth");
  43.   tabBar.mTabMinWidth  = gTabmixPrefs.getIntPref("browser.tabs.tabMinWidth");
  44.   if (tabBar.mTabMaxWidth < tabBar.mTabMinWidth) {
  45.     tabxPrefs.setBoolPref("setDefault", true);
  46.     gTabmixPrefs.setIntPref("browser.tabs.tabMaxWidth", tabBar.mTabMinWidth);
  47.     gTabmixPrefs.setIntPref("browser.tabs.tabMinWidth", tabBar.mTabMaxWidth);
  48.     tabxPrefs.clearUserPref("setDefault"); // this trigger TMupdateSettings
  49.     return;
  50.   }
  51.   gWidthFitTitle = TMP_getBoolPref(tabxBranch, "flexTabs", false) &&
  52.                    (tabBar.mTabMaxWidth != tabBar.mTabMinWidth);
  53.   alwaysNewTab =     TMP_getIntPref (tabxBranch, "speLink",          0);
  54.   var currentVisible = start ? true : tabBar.isTabVisible(tabBrowser.mCurrentTab._tPos);
  55.  
  56.   if (!gisToolbarMode) {
  57.     var direction = gTabbarPosition == 1 ? "rtl" : "ltr";
  58.     tabBrowser.mTabBox.setAttribute("dir", direction);
  59.   }
  60.  
  61.   if (tabBar.mAllTabsPopup) { // mAllTabsPopup will remove in firfox 3.5 or later
  62.      // fix bug in positioning the popup off screen or on the button when window is not maximize or when tab bar is in the bottom
  63.      TMP_setItem(tabBar.mAllTabsPopup, "position",
  64.             (window.windowState != window.STATE_MAXIMIZED || gTabbarPosition == 1) ? "start_before" : "after_end");
  65.   }
  66.  
  67.   switch ( tabscroll ) {
  68.     case 0:
  69.       tabBar.setAttribute("flowing", "singlebar");
  70.       tabBar.overflow = false;
  71.       break;
  72.     case 1:
  73.       if (!tabBar.hasAttribute("scrollbutton-up"))
  74.         tabBar.setAttribute("scrollbutton-up", "left");
  75.     case 3:
  76.       if (tabBar.getAttribute("flowing") != "scrollbutton") {
  77.         tabBar.setAttribute("flowing", "scrollbutton");
  78.         tabBar.overflow = tabBar.canScrollTabsLeft || tabBar.canScrollTabsRight;
  79.       }
  80.       break;
  81.     case 2:
  82.       if (tabBar.getAttribute("flowing") != "multibar") {
  83.          tabBar.setAttribute("flowing", "multibar");
  84.          if (!start || tabBar.collapsedTabs > 0)
  85.            tabBar.collapsedTabs = 0;
  86.       }
  87.       break;
  88.   }
  89.  
  90.   /* Fix problem with multi-row when tab width is very small
  91.      need to findout how to remove extra space when display is block or inline
  92.      then we don't have to fix the tab height
  93.   */
  94.   if (tabscroll == 2 && tabBar.mTabMaxWidth < 50)
  95.     TMP_setItem(tabBar, "inline", true);
  96.   else
  97.     tabBar.removeAttribute("inline");
  98.  
  99.   if (tabscroll != 1 && tabBar.hasAttribute("scrollbutton-up"))
  100.     tabBar.removeAttribute("scrollbutton-up");
  101.  
  102.   var showCloseButton = TMP_getBoolPref(tabxBranch, "tabXMode.enable",  true);
  103.   for (i = 0; i < tabBar.childNodes.length; i++) {
  104.     var aTab = tabBar.childNodes[i];
  105.  
  106.     if (!aTab.hasAttribute("faviconized")) {
  107.        aTab.maxWidth = tabBar.mTabMaxWidth;
  108.        aTab.style.maxWidth = tabBar.mTabMaxWidth + "px";
  109.        aTab.minWidth = tabBar.mTabMinWidth;
  110.     }
  111.     if (gWidthFitTitle) {
  112.       if (aTab.hasAttribute("width")) aTab.removeAttribute("width");
  113.       if (aTab.hasAttribute("flex")) aTab.removeAttribute("flex");
  114.     } else {
  115.       TMP_setItem(aTab, "width", "0");
  116.       TMP_setItem(aTab, "flex", "100");
  117.     }
  118.  
  119.     if ( alwaysNewTab == 1) {
  120.       // when we can change user locked / unlock tabs
  121.       var changeUserLockStatus = (start && aTab.getAttribute("_locked") != "false") ||
  122.                           (!start && tabBar.getAttribute("lockAllTab") != "true")
  123.       if (changeUserLockStatus) {
  124.         aTab.setAttribute("locked", "true");
  125.         aTab.removeAttribute("_locked");
  126.       }
  127.     }
  128.     else if ( tabBar.getAttribute("lockAllTab") == "true" ) {
  129.       aTab.removeAttribute("locked");
  130.       aTab.removeAttribute("_locked");
  131.     }
  132.  
  133.     SessionManager.updateTabProp(aTab);
  134.  
  135.     if (showCloseButton) {
  136.        var closeButtonOptions = ["no-button","always","showhover","current","current_hover","always"];
  137.        aTab.setAttribute("tabx", closeButtonOptions[addtabx]);
  138.     }
  139.     else
  140.        aTab.removeAttribute("tabx");
  141.  
  142.     if (tabxleft)
  143.        aTab.setAttribute("tabxleft", "on");
  144.     else
  145.        aTab.removeAttribute("tabxleft");
  146.   }
  147.  
  148.   // show on tabbar
  149.   tabBar.setAttribute("closebutton", !TMP_getBoolPref(tabxBranch, "hideTabBarButton", true));
  150.   tabBar.setAttribute("hideAllTabsButton", TMP_getBoolPref(tabxBranch, "hideAllTabsButton", false));
  151.   tabBar.setAttribute("tabBarSpace", TMP_getBoolPref(tabxBranch, "tabBarSpace", false));
  152.   tabBar.setAttribute("extraIcons", TMP_getBoolPref(tabxBranch, "extraIcons", true));
  153.   tabBar.setAttribute("lockAllTab", alwaysNewTab == 1 );
  154.   var showNewTabButton = tabxPrefs.getBoolPref("newTabButton");
  155.   tabBar.setAttribute("newTabButton", showNewTabButton);
  156.   tabBar._checkNewtabButtonvisibility = gIsFirefox35 && showNewTabButton && tabxPrefs.getIntPref("newTabButton.position") == 2;
  157.  
  158.   // user changed maxRow number
  159.   if (tabBar.tabstrip.style.maxHeight && gBrowser.getStripVisibility() && tabBar.collapsedTabs > 0) {
  160.     var currentMaxRow = tabBar.lastTabRowNumber;
  161.     var rowDiff = tabBar.maxRow - currentMaxRow;
  162.     while (tabBar.collapsedTabs > 0 && rowDiff > 0) {
  163.       tabBar.rowScroll(-1);
  164.       rowDiff--;
  165.     }
  166.   }
  167.  
  168.   if (start && "isLoadHomePage" in window) {
  169.     window.setTimeout(tabBarScrollStatus, 0);
  170.     delete window.isLoadHomePage;
  171.   }
  172.   else
  173.     tabBarScrollStatus();
  174.  
  175.   window.setTimeout( function TMupdateSettings_adjustScroll(_currentVisible) {
  176.       if (_currentVisible) {
  177.         tabBar.ensureTabIsVisible(tabBar.selectedIndex);
  178.       }
  179.       tabBar.adjustScrollTabsLeft();
  180.       tabBar.adjustScrollTabsRight();
  181.       checkBeforeAndAfter();
  182.      //XXX underline the label bleed over the end when tab is to small and we have close button on the tab !!!!????
  183.       toggleUnderlineTabsLabel();
  184.   }, 50, currentVisible);
  185.  
  186. }
  187.  
  188. /*
  189. XXX underline the label bleed over the end when tab is to small and we have close button on the tab !!!!????
  190. workaround is to remove underline when tab is too small
  191.  
  192. when to set hideunderline
  193. A. tababr is visibile
  194. B. !gWidthFitTitle and width < 70
  195. C. gWidthFitTitle and maxwidth < 70
  196. */
  197. function toggleUnderlineTabsLabel() {
  198.   var tabBar = gBrowser.mTabContainer;
  199.   if (tabBar.boxObject.width == 0)
  200.     return;
  201.  
  202.   var _tinywidth;
  203.   if (tabBar.mTabMaxWidth < 70)
  204.     _tinywidth = true;
  205.   else {
  206.     var tab = tabBar.childNodes[tabBar.collapsedTabs];
  207.     if (tab)
  208.       _tinywidth = !gWidthFitTitle && tab.boxObject.width < 70;
  209.   }
  210.   TMP_setItem(tabBar, "hideunderline", _tinywidth || null);
  211. }
  212.  
  213. var gVisibleRows = 1;
  214. function tabBarScrollStatus () {
  215.   /*
  216.   * multibar is not affected by RTL direction we only need to consider RTL when we are in one row mode
  217.   */
  218.   var tabBar = gBrowser.mTabContainer;
  219.   if (tabBar.boxObject.width == 0)
  220.     return;
  221.  
  222.   tabBar.adjustNewtabButtonvisibility();
  223.   tabBar.adjustScrollTabsLeft();
  224.   var multibar, lastTabRow, currentMultibar = tabBar.getAttribute("multibar") || null;
  225.   if (tabscroll == 2) {
  226.     // we need to check this when last row of tabs is empty and we still have hidden row on top
  227.     // this can occur when we close last tab in the last row or when some tab changed width
  228.     if ( tabBar.getAttribute("multibar") == "scrollbar" &&
  229.             tabBar.collapsedTabs > 0 && tabBar.lastTabRowNumber < tabBar.maxRow) {
  230.         tabBar.rowScroll(-1);
  231.     }
  232.     [multibar, lastTabRow] = getMultiRowAttribute();
  233.   }
  234.   else {
  235.     [multibar, lastTabRow] = [null, 1];
  236.   }
  237.  
  238.   if (currentMultibar != multibar) {
  239.     TMP_setItem(tabBar, "multibar", multibar); // if multibar is null we remove the attribute
  240.     if (gisToolbarMode)
  241.       TMP_setItem("tabs-toolbar", "multibar", multibar);
  242.   }
  243.   setTabBarHeight(lastTabRow);
  244.  
  245.   tabBar.adjustScrollTabsRight();
  246. }
  247.  
  248. function getMultiRowAttribute () {
  249.   var tabBar = gBrowser.mTabContainer;
  250.   var maxRow = tabBar.maxRow;
  251.   if (tabBar.collapsedTabs > 0)
  252.     return ["scrollbar", maxRow];
  253.  
  254.   if (!gBrowser.getStripVisibility())
  255.     return [null, 1];
  256.  
  257.   var lastTabRow;
  258.   if (tabBar.hasAttribute("multibar")) {
  259.     tabBar.adjustNewtabButtonvisibility();
  260.     var lastTabRow = tabBar.lastTabRowNumber;
  261.     if (lastTabRow == 1)
  262.       return [null, 1]; // removeAttribute "multibar"
  263.     if (lastTabRow > tabBar.maxRow)
  264.       return ["scrollbar", maxRow];
  265.   }
  266.   else {
  267.     if (tabBar.lastTabVisible)
  268.       return [null, 1]; // removeAttribute "multibar"
  269.     // init multibar
  270.     tabBar.setAttribute("multibar", "true");
  271.     tabBar.adjustNewtabButtonvisibility();
  272.     // remember that tabBar.lastTabRowNumber may change when we set "multibar" to true
  273.     lastTabRow = tabBar.lastTabRowNumber;
  274.     if (lastTabRow > tabBar.maxRow)
  275.       return ["scrollbar", maxRow];
  276.   }
  277.  
  278.   return ["true", lastTabRow];
  279. }
  280.  
  281. var windowStyle = {exist:false, value:null};
  282. var tabBarHeights = {};
  283. function setTabBarHeight (aRows) {
  284.   if (gVisibleRows == aRows)
  285.     return;
  286.  
  287.   gVisibleRows = aRows;
  288.   var tabBar = gBrowser.mTabContainer;
  289.   var tabstrip = tabBar.tabstrip;
  290.  
  291.   if (aRows == 1) {
  292.     if (tabstrip.hasAttribute("style")) {
  293.       tabstrip.style.removeProperty("max-height");
  294.       tabstrip.style.removeProperty("height");
  295.     }
  296.     if (tabBar.hasAttribute("style")) {
  297.       tabBar.style.removeProperty("max-height");
  298.       tabBar.style.removeProperty("height");
  299.     }
  300.     if (windowStyle.exist)
  301.       TMP_setItem(document.getElementById("main-window"), "style", windowStyle.value);
  302.     return;
  303.   }
  304.  
  305.   var newHeight;
  306.   if (aRows in tabBarHeights)
  307.     newHeight = tabBarHeights[aRows];
  308.   else {
  309.     if (tabBar.tabstripInnerbox) {
  310.       if (tabBar.getAttribute("multibar") == "scrollbar")
  311.         // we never get this..... we always get row x with multibar=true before we get to the max row
  312.         // once we get to max row and set multibar=scrollbar gVisibleRows == lastTabRow in tabBarScrollStatus
  313.         newHeight = tabBarHeights[aRows] = (tabBar.tabstripInnerbox.boxObject.height/tabBar.lastTabRowNumber) * aRows;
  314.       else
  315.         newHeight = tabBarHeights[aRows] = tabBar.tabstripInnerbox.boxObject.height;
  316.     }
  317.     else
  318.       newHeight = tabBarHeights[aRows] = getRowHeight() * aRows;
  319.   }
  320.  
  321.   if (tabstrip.style.maxHeight != tabstrip.style.height || tabstrip.style.maxHeight != newHeight + "px") {
  322.     tabstrip.style.setProperty("max-height",newHeight + "px", "important");
  323.     tabstrip.style.setProperty("height",newHeight + "px", "important");
  324.     var tabsBottom = document.getAnonymousElementByAttribute(tabBar, "class", "tabs-bottom");
  325.     var tabsBottomHeight = tabsBottom ? tabsBottom.boxObject.height : 0;
  326.     var newTabbarHeight = newHeight + tabsBottomHeight;
  327.     // override fixed height set by theme to .tabbrowser-tabs class
  328.     if (tabBar.boxObject.height < newTabbarHeight || tabBar.hasAttribute("style")) {
  329.       tabBar.style.setProperty("max-height",newTabbarHeight + "px", "important");
  330.       tabBar.style.setProperty("height",newTabbarHeight + "px", "important");
  331.     }
  332.  
  333.     // experimental - for theme that put tababr above the menus
  334.     // curently its only work with Vfox3 theme
  335.     if (tabstrip.boxObject.y <= gNavToolbox.boxObject.y) {
  336.       let skin = gTabmixPrefs.getCharPref("general.skins.selectedSkin");
  337.       let themes = /^(Vfox3)/;
  338.       if (themes.test(skin)) {
  339.         let mWin = document.getElementById("main-window");
  340.         if (!windowStyle.exist) {
  341.           windowStyle.exist = true;
  342.           windowStyle.value = mWin.hasAttribute("style") ? mWin.getAttribute("style") : null;
  343.         }
  344.         mWin.style.setProperty("padding-top", newTabbarHeight + "px", "important");
  345.       }
  346.     }
  347.   }
  348. }
  349.  
  350. // Update beforeselected and afterselected attribute when we are in multi-row mode
  351. function checkBeforeAndAfter() {
  352.   var tabBar = gBrowser.mTabContainer;
  353.   if (!tabBar.hasAttribute("multibar"))
  354.      return;
  355.  
  356.   var top = tabBar.topTabY;
  357.   var tab = tabBar.selectedItem, tabRow = tabBar.getTabRowNumber(tab, top);
  358.   var prev = tab.previousSibling, next = tab.nextSibling;
  359.   if (prev && prev.localName == "tab") {
  360.     TMP_setItem(prev, "beforeselected", tabRow == tabBar.getTabRowNumber(prev, top) ? true : null);
  361.   }
  362.   if (next && next.localName == "tab") {
  363.     TMP_setItem(next, "afterselected", tabRow == tabBar.getTabRowNumber(next, top) ? true : null);
  364.   }
  365. }
  366.  
  367. var gRowHeight = null;
  368. function getRowHeight () {
  369.   if (gRowHeight)
  370.     return gRowHeight;
  371.  
  372.   var tabBar = gBrowser.mTabContainer;
  373.   var tabs = tabBar.childNodes;
  374.  
  375.   var firstTab = tabs[tabBar.collapsedTabs];
  376.   var top = tabBar.topTabY;
  377.   var lastTabRow = tabBar.lastTabRowNumber;
  378.   if (lastTabRow == 1) { // one row
  379.     if (firstTab.getAttribute("selected") == "true")
  380.       return tabBar.lastChild.boxObject.height;
  381.     else
  382.       return firstTab.boxObject.height;
  383.   }
  384.   else if (lastTabRow == 2) { // multi-row
  385.     if (tabBar.lastChild.getAttribute("selected") == "true") {
  386.       // check if previous to last tab in the 1st row
  387.       // this happen when the selected tab is the first tab in the 2nd row
  388.       var prev = tabBar.lastChild.previousSibling;
  389.       if (prev && tabBar.getTabRowNumber(prev, top) == 1)
  390.         return tabBar.lastChild.boxObject.height;
  391.       else
  392.         gRowHeight = prev.baseY - firstTab.baseY;
  393.     }
  394.     else if (firstTab.getAttribute("selected") == "true") {
  395.       // check if 2nd visible tab is in the 2nd row
  396.       // (not likely that user set tab width to more then half screen width)
  397.       var next = firstTab.nextSibling;
  398.       if (next && tabBar.getTabRowNumber(next, top) == 2)
  399.         return tabBar.lastChild.boxObject.height;
  400.       else
  401.         gRowHeight = tabBar.lastChild.baseY - next.baseY;
  402.     }
  403.     else
  404.       gRowHeight = tabBar.lastChild.baseY - firstTab.baseY;
  405.  
  406.     return gRowHeight;
  407.   }
  408.  
  409.   // Just in case we missed something in the above code............
  410.   var i, j;
  411.   i = j = tabBar.collapsedTabs;
  412.   if ( tabs[j] && tabs[j].getAttribute("selected") == "true" )
  413.     j++;
  414.   while (inSameRow( tabs.item(i), tabs.item(j) ) )
  415.     i++;
  416.  
  417.   if ( !tabs[i] ) // only one row
  418.     if ( tabs[j] )
  419.       return tabs[j].boxObject.height;
  420.     else
  421.       return tabs[0].boxObject.height;
  422.  
  423.   if ( tabs[i].getAttribute("selected") == "true" )
  424.     i++;
  425.   if ( !tabs[i] )
  426.     return tabs[i-1].boxObject.height;
  427.  
  428.   gRowHeight = tabs[i].baseY - tabs[j].baseY;
  429.   return gRowHeight;
  430. }
  431.  
  432. function inSameRow (tab1, tab2) {
  433.   if ( !tab1 || !tab2 )
  434.     return false;
  435.  
  436.   var tabBar = gBrowser.mTabContainer;
  437.   var top = tabBar.topTabY;
  438.   return tabBar.getTabRowNumber(tab1, top) == tabBar.getTabRowNumber(tab2, top);
  439. }
  440.  
  441. // call by resize event on content
  442. function tabBarWidthChange (aEvent) {
  443.   var tabBar = gBrowser.mTabContainer;
  444.  
  445.   if (tabBar.mAllTabsPopup) { // mAllTabsPopup will remov in firfox 3.5 or later
  446.     // fix bug in positioning the popup off screen or on the button when window is not maximize or when tab bar is in the bottom
  447.     TMP_setItem(tabBar.mAllTabsPopup, "position",
  448.               (window.windowState != window.STATE_MAXIMIZED || gTabbarPosition == 1) ? "start_before" : "after_end");
  449.   }
  450.  
  451.   // we don't need to update scroll status when tab bar is in collapsed mode
  452.   if (tabBar.boxObject.width == 0 || window.windowState == window.STATE_MINIMIZED)
  453.     return;
  454.  
  455.   // we don't need to update scroll status if resize event not trigger by width change
  456.   if ( gTabBarWidth == tabBar.boxObject.width)
  457.     return;
  458.  
  459.   var tabs = tabBar.childNodes;
  460.  
  461.   if (addtabx == 5 && !gWidthFitTitle)
  462.     adjustOn2ndTab.closeButton(1);
  463.  
  464.   var oldCollapsed = tabBar.collapsedTabs;
  465.   var i = 0;
  466.   if ( tabscroll != 2 && gTabBarWidth < tabBar.boxObject.width ) {
  467.     while ( tabs[ oldCollapsed + i ] &&
  468.             tabs[ oldCollapsed + i ].boxObject.screenX + tabs[ oldCollapsed + i ].boxObject.width <
  469.               tabBar.tabstrip.boxObject.screenX + tabBar.tabstrip.boxObject.width ) {
  470.       i++;
  471.     }
  472.     if (tabBar.collapsedTabs > 0)
  473.       tabBar.collapsedTabs = 0;
  474.     var index = oldCollapsed + i - 1;
  475.     tabBar.ensureTabIsVisible(index);
  476.     tabBarScrollStatus();
  477.   }
  478.   else if ( tabscroll == 2 ) {
  479.     if (tabBar.collapsedTabs > 0)
  480.       tabBar.collapsedTabs = 0;
  481.     tabBarScrollStatus();
  482.     tabBar.ensureTabIsVisible(tabBar.lastChild._tPos);
  483.     tabBar.ensureTabIsVisible( oldCollapsed );
  484.     checkBeforeAndAfter();
  485.   }
  486.   else
  487.     tabBarScrollStatus();
  488.  
  489.   gTabBarWidth = tabBar.boxObject.width;
  490. }
  491.  
  492. // Function to catch changes to Tab Mix preferences and update existing windows and tabs
  493. //
  494. var gTMPprefObserver = {
  495.   init: function() {
  496.     var pref = "setDefault"
  497.     if (tabxPrefs.prefHasUserValue(pref))
  498.       tabxPrefs.clearUserPref(pref)
  499.     pref = "PrefObserver.error";
  500.     if (tabxPrefs.prefHasUserValue(pref))
  501.       tabxPrefs.clearUserPref(pref)
  502.  
  503.     if ("TreeStyleTabBrowser" in window)
  504.       this.OBSERVING.push("extensions.treestyletab.tabbar.position");
  505.  
  506.     try {
  507.       // add Observer
  508.       let prefSvc = gTabmixPrefs;
  509.       for (var i = 0; i < this.OBSERVING.length; ++i)
  510.         prefSvc.addObserver(this.OBSERVING[i], this, true);
  511.     }
  512.     catch(e) {
  513.       tmLog("prefs-Observer failed to attach:" + "\n" + e);
  514.       tabxPrefs.setBoolPref(pref, true);
  515.     }
  516.   },
  517.  
  518.   // nsISupports interface implementation -- for weak-reference by pref-observer service
  519.   QueryInterface: function(iid) {
  520.     if (!iid.equals(Components.interfaces.nsISupports)
  521.         && !iid.equals(Components.interfaces.nsISupportsWeakReference)
  522.         && !iid.equals(Components.interfaces.nsIObserver)) {
  523.       dump("Tab Mix Plus pref-observer factory object: QI unknown interface: " + iid + "\n");
  524.       throw Components.results.NS_ERROR_NO_INTERFACE;
  525.     }
  526.     return this;
  527.   },
  528.  
  529.   OBSERVING: ["extensions.tabmix.",
  530.               "browser.tabs.autoHide",
  531.               "browser.tabs.tabMinWidth",
  532.               "browser.tabs.tabMaxWidth",
  533.               "browser.tabs.tabClipWidth",
  534.               "browser.sessionstore.max_tabs_undo",
  535.               "browser.warnOnRestart",
  536.               "browser.warnOnQuit",
  537.               "browser.sessionstore.resume_from_crash",
  538.               "browser.startup.page",
  539.               "browser.link.open_external",
  540.               "browser.link.open_newwindow.restriction",
  541.               "browser.link.open_newwindow",
  542.               "browser.ctrlTab.previews"],
  543.  
  544.   // removes the observer-object from service -- called when the window is no longer open
  545.   removeObservers: function() {
  546.     let prefSvc = gTabmixPrefs;
  547.     for (var i = 0; i < this.OBSERVING.length; ++i)
  548.       prefSvc.removeObserver(this.OBSERVING[i], this);
  549.   },
  550.  
  551.   /* Observer-function */
  552.   /* subject: [wrapped nsISupports :: nsIPrefBranch], nsIPrefBranch Internal
  553.      topic: "changed"*/
  554.   observe: function TMP_pref_observer(subject, topic, prefName) {
  555.     // if we don't have a valid window (closed)
  556.     if ( !(typeof(document) == 'object' && document) ) {
  557.       this.removeObservers(); // remove the observer..
  558.       return;  // ..and don't continue
  559.     }
  560.  
  561.     var prefValue;
  562.     switch (prefName) {
  563.       case "extensions.tabmix.linkTarget":
  564.  
  565.       case "extensions.tabmix.opentabfor.bookmarks":
  566.       case "extensions.tabmix.opentabfor.history":
  567.       case "extensions.tabmix.opentabfor.urlbar":
  568.       case "extensions.tabmix.middlecurrent":
  569.       case "extensions.tabmix.inversefocusLinks":
  570.       case "extensions.tabmix.inversefocusOther":
  571.  
  572.       case "extensions.tabmix.loadNewInBackground":
  573.       case "extensions.tabmix.loadUrlInBackground":
  574.       case "extensions.tabmix.loadSearchInBackground":
  575.       case "extensions.tabmix.loadDuplicateInBackground":
  576.       case "extensions.tabmix.loadBookmarksGroupInBackground":
  577.  
  578.       case "extensions.tabmix.filetype":
  579.       case "extensions.tabmix.warnAboutClosingTabs.timeout":
  580.       case "extensions.tabmix.sessions.crashed":
  581.       case "extensions.tabmix.disableIncompatible":
  582.  
  583.       case "extensions.tabmix.appearance_tab":
  584.       case "extensions.tabmix.selected_tab":
  585.       case "extensions.tabmix.selected_sub_tab1":
  586.       case "extensions.tabmix.selected_sub_tab2":
  587.       case "extensions.tabmix.selected_sub_tab3":
  588.       case "extensions.tabmix.selected_sub_tab4":
  589.       case "extensions.tabmix.selected_sub_tab5":
  590.       case "extensions.tabmix.selected_sub_tab6":
  591.  
  592.       case "extensions.tabmix.reload_time":
  593.       case "extensions.tabmix.custom_reload_time":
  594.       case "extensions.tabmix.resume_session_once":
  595.         break;
  596.       case "extensions.tabmix.undoCloseButton.menuonly":
  597.         TMP_ClosedTabs.setButtonType(gTabmixPrefs.getBoolPref(prefName));
  598.         break;
  599.       case "extensions.tabmix.focusTab":
  600.           gTabmixPrefs.setBoolPref("browser.tabs.selectOwnerOnClose", gTabmixPrefs.getIntPref(prefName) == 2);
  601.         break;
  602.       case "extensions.tabmix.disableF9Key":
  603.         this.toggleKey("key_tm_toggleFLST", prefName);
  604.         break;
  605.       case "extensions.tabmix.disableF8Key":
  606.         this.toggleKey("key_tm_slideShow", prefName);
  607.         break;
  608.       case "extensions.tabmix.hideIcons":
  609.         this.setMenuIcons();
  610.         break;
  611.       // tab appearnce
  612.       case "extensions.tabmix.currentTab":
  613.       case "extensions.tabmix.unreadTab":
  614.       case "extensions.tabmix.otherTab":
  615.         this.toggleTabStyles(prefName);
  616.         break;
  617.       case "extensions.tabmix.styles.currentTab":
  618.       case "extensions.tabmix.styles.unreadTab":
  619.       case "extensions.tabmix.styles.otherTab":
  620.       case "extensions.tabmix.styles.progressMeter":
  621.         this.setTabStyles(prefName);
  622.         break;
  623.       case "extensions.tabmix.progressMeter":
  624.       case "extensions.tabmix.noprogress":
  625.         var progressMeterOnTabs = tabxPrefs.getBoolPref("progressMeter");
  626.         gBrowser.mTabContainer.setAttribute("progressMeter", progressMeterOnTabs);
  627.         document.getElementById("statusbar-progresspanel").setAttribute("hidden", tabxPrefs.getBoolPref("noprogress") && progressMeterOnTabs);
  628.         break;
  629.       case "extensions.tabmix.flexTabs":
  630.       case "extensions.tabmix.tabXMode":
  631.         TMupdateSettings(false);
  632.       case "extensions.tabmix.keepLastTab":
  633.       case "browser.tabs.tabClipWidth":
  634.         adjustOn2ndTab.closeButton(1);
  635.         break;
  636.       case "extensions.tabmix.tabBarPosition":
  637.          if (this.tabBarPositionChanged(gTabmixPrefs.getIntPref(prefName))) {
  638.            if (window.fullScreen)
  639.              TMP_eventListener.onFullScreen(true);
  640.            TMupdateSettings(false);
  641.          }
  642.         break;
  643.       case "extensions.tabmix.useGreyCloseButton":
  644.         this.tabCloseButton();
  645.         break;
  646.       case "extensions.tabmix.undoClose":
  647.         if (!tabxPrefs.getBoolPref("undoClose")) {
  648.           gTabmixPrefs.setIntPref("browser.sessionstore.max_tabs_undo", 0);
  649.         }
  650.         else if (gTabmixPrefs.getIntPref("browser.sessionstore.max_tabs_undo") == 0)
  651.           gTabmixPrefs.clearUserPref("browser.sessionstore.max_tabs_undo");
  652.         break;
  653.       case "browser.sessionstore.max_tabs_undo":
  654.         prefValue = gTabmixPrefs.getIntPref(prefName);
  655.         while (prefValue < TMP_ClosedTabs.count) {
  656.           TMP_ClosedTabs.ssIsON ? TMP_ClosedTabs.getClosedTabAtIndex(TMP_ClosedTabs.count - 1) : getClosedTab("delete", TMP_ClosedTabs.count - 1);
  657.         }
  658.         if (tabxPrefs.getBoolPref("undoClose") != (prefValue > 0))
  659.           tabxPrefs.setBoolPref("undoClose", prefValue > 0);
  660.         TMP_ClosedTabs.setButtonDisableState();
  661.         break;
  662.       case "browser.warnOnRestart":
  663.       case "browser.warnOnQuit":
  664.       case "browser.sessionstore.resume_from_crash":
  665.         if (!gTabmixPrefs.getBoolPref(prefName))
  666.           return;
  667.  
  668.         var TMP_sessionManager_enabled = tabxPrefs.getBoolPref("sessions.manager") ||
  669.                          tabxPrefs.getBoolPref("sessions.crashRecovery");
  670.         if (TMP_sessionManager_enabled)
  671.           gTabmixPrefs.setBoolPref(prefName, false);
  672.         break;
  673.       case "browser.startup.page":
  674.         if (gTabmixPrefs.getIntPref(prefName) != 3)
  675.           return;
  676.         TMP_sessionManager_enabled = tabxPrefs.getBoolPref("sessions.manager") ||
  677.                          tabxPrefs.getBoolPref("sessions.crashRecovery");
  678.  
  679.         if (TMP_sessionManager_enabled)
  680.           gTabmixPrefs.setIntPref(prefName, 1);
  681.         break;
  682.       case "extensions.tabmix.sessions.manager":
  683.       case "extensions.tabmix.sessions.crashRecovery":
  684.         TMP_SessionStore.setService(2, false);
  685.       case "extensions.tabmix.sessions.save.closedtabs":
  686.       case "extensions.tabmix.sessions.save.history":
  687.       case "extensions.tabmix.sessionToolsMenu":
  688.       case "extensions.tabmix.closedWinToolsMenu":
  689.         SessionManager.updateSettings();
  690.         break;
  691.       case "extensions.tabmix.optionsToolMenu":
  692.         document.getElementById("tabmix-menu").hidden = !gTabmixPrefs.getBoolPref(prefName);
  693.         break;
  694.       case "browser.link.open_external":
  695.       case "browser.link.open_newwindow.restriction":
  696.       case "browser.link.open_newwindow":
  697.         this.setLink_openPrefs();
  698.         break;
  699.       case "extensions.tabmix.singleWindow":
  700.         this.setSingleWindowUI();
  701.         break;
  702.       case "extensions.tabmix.hideTabbar":
  703.         this.setAutoHidePref();
  704.         this.setTabBarVisibility(false);
  705.         break;
  706.       case "browser.tabs.autoHide":
  707.         this.setAutoHidePref();
  708.         break;
  709.       case "extensions.tabmix.newTabButton.position":
  710.         this.changeNewTabButtonSide(gTabmixPrefs.getIntPref(prefName));
  711.         break;
  712.       case "browser.ctrlTab.previews":
  713.       case "extensions.tabmix.lasttab.tabPreviews":
  714.       case "extensions.tabmix.lasttab.favorLeftToRightOrdering":
  715.       case "extensions.tabmix.lasttab.respondToMouseInTabList":
  716.       case "extensions.tabmix.lasttab.showTabList":
  717.         TMP_LastTab.ReadPreferences();
  718.         break;
  719.       case "extensions.treestyletab.tabbar.position":
  720.         TMP_setDragEvents(false);
  721.         break;
  722.       case "extensions.tabmix.reloadEvery.onReloadButton":
  723.         this.showReloadEveryOnReloadButton();
  724.         break;        
  725.       default:
  726.         TMupdateSettings(false);
  727.     }
  728.  
  729.   },
  730.  
  731.   tabCloseButton: function() {
  732.     /* Mac from Firefox 2 and Linux from Firefox 3 don't use close.png */
  733.     if (/^Linux/.test(navigator.platform))
  734.        return;
  735.  
  736.     // we have some exeptions here....
  737.     // classiccompact theme
  738.     // .... if we find more we need to add theme here or maybe add some pref??
  739.     if (/^Mac/.test(navigator.platform) && gTabmixPrefs.getCharPref("general.skins.selectedSkin") != "classiccompact")
  740.        return;
  741.  
  742.     try {
  743.       var useNewCloseIcon = tabxPrefs.getBoolPref("useGreyCloseButton");
  744.     }
  745.     catch(er) { useNewCloseIcon = true; }
  746.     if (useNewCloseIcon) {
  747.       gBrowser.mTabContainer.setAttribute("closeIcon", "v3");
  748.     }
  749.     else
  750.       gBrowser.mTabContainer.removeAttribute("closeIcon");
  751.   },
  752.  
  753.   toggleKey: function(keiID, prefName) {
  754.     var key = document.getElementById(keiID);
  755.     if (TMP_getBoolPref("", prefName, false)) {
  756.       if (key.hasAttribute("oncommand"))
  757.         key.removeAttribute("oncommand");
  758.     } else
  759.       key.setAttribute("oncommand", key.getAttribute("TM_oncommand"));
  760.   },
  761.  
  762.   colorRules: {},
  763.   createColorRules: function TMP_PO_createColorRules() {
  764.     // find tab.css to insert our color rules into it.
  765.     // insert our rules into document.styleSheets[0] cause problem with other extensions
  766.     var ss = null;
  767.     for (var i = 0; i < document.styleSheets.length; ++i) {
  768.       if (document.styleSheets[i].href == "chrome://tabmixplus/skin/tab.css") {
  769.         ss = document.styleSheets[i];
  770.         break;
  771.       }
  772.     }
  773.     if (!ss)
  774.       ss = document.styleSheets[document.styleSheets.length-1];
  775.  
  776.     this.tabStyleSheet = ss;
  777.  
  778.     var styleRules = {
  779.       currentTab:    { text:  '.tabbrowser-tabs[currentTab=true][useCurrentColor=true] .tabbrowser-tab[selected="true"] .tab-text { color: #colorCode;}',
  780.                        bg  :  '.tabbrowser-tabs[currentTab=true][useCurrentBGColor=true] .tabbrowser-tab[selected="true"],'+
  781.                               '.tabbrowser-tabs[currentTab=true][useCurrentBGColor=true][tabonbottom] .tabs-bottom {background-color: #colorCode !important;}'},
  782.       unreadTab:     { text:  '.tabbrowser-tabs[unreadTab=true][useUnreadColor=true] .tabbrowser-tab:not([selected="true"]):not([visited]) .tab-text { color: #colorCode;}',
  783.                        bg:    '.tabbrowser-tabs[unreadTab=true][useUnreadBGColor=true] .tabbrowser-tab:not([selected="true"]):not([visited]){ background-color: #colorCode !important;}'},
  784.       otherTab:      { text:  '.tabbrowser-tabs[otherTab=true][useOtherColor=true]:not([unreadTab=true]) .tabbrowser-tab:not([selected="true"]) .tab-text,' +
  785.                               '.tabbrowser-tabs[otherTab=true][useOtherColor=true] .tabbrowser-tab:not([selected="true"])[visited] .tab-text { color: #colorCode;}',
  786.                        bg:    '.tabbrowser-tabs[otherTab=true][useOtherBGColor=true]:not([unreadTab=true]) .tabbrowser-tab:not([selected="true"]),' +
  787.                               '.tabbrowser-tabs[otherTab=true][useOtherBGColor=true] .tabbrowser-tab:not([selected="true"])[visited]{ background-color: #colorCode !important;}'},
  788.       progressMeter: { bg:    '.tabbrowser-tabs[useProgressColor=true] .tabbrowser-tab .progress-bar { background-color: #colorCode !important;}'}
  789.     }
  790.  
  791.     // Charter Toolbar extension add Object.prototype.toJSONString
  792.     // that break the use      "for (var rule in styleRules)"
  793.     var j, rule;
  794.     var rules = ["currentTab", "unreadTab", "otherTab", "progressMeter"];
  795.     for (var j = 0; j < rules.length; j++) {
  796.       rule = rules[j];
  797.       this.setTabStyles("extensions.tabmix.styles." + rule, true);
  798.       var prefValues = this.tabStylePrefs[rule];
  799.       if (!prefValues)
  800.         continue;
  801.       var newRule, index;
  802.       if (rule !=  "progressMeter") {
  803.         newRule = styleRules[rule].text.replace("#colorCode",prefValues.textColor);
  804.         index = ss.insertRule(newRule, ss.cssRules.length);
  805.         this.colorRules[rule] = ss.cssRules[index];
  806.       }
  807.       newRule = styleRules[rule].bg.replace("#colorCode",prefValues.bgColor);
  808.       index = ss.insertRule(newRule, ss.cssRules.length);
  809.       this.colorRules[rule + "bg"] = ss.cssRules[index];
  810.       if (rule != "progressMeter")
  811.         this.toggleTabStyles(rule);
  812.  
  813.       // in 0.3.0.605 we changed tab color from old pref to new pref
  814.       // old pref "extensions.tabmix.currentColor" type integer
  815.       // new pref "extensions.tabmix.currentColorCode" type string
  816.       //
  817.       // in 0.3.7.4 2008-12-24 we combined all style pref into one per type
  818.       // extensions.tabmix.styles.[TYPE NAME]
  819.       gPreventUpdate = true;
  820.       var ruleName = rule.replace(/Tab|Meter/,"");
  821.       var attrib = ruleName.charAt(0).toUpperCase() + ruleName.substr(1);
  822.       var oldPrefs = {italic: "italic" + attrib, bold: "bold" + attrib, underline: "underline" + attrib,
  823.                        text: "use"+ attrib + "Color", textColor: ruleName + "ColorCode", textColorOLD: ruleName + "Color"}
  824.       const pBranch = Components.interfaces.nsIPrefBranch;
  825.       var needToUpdatePref = false;
  826.       var prefsToChange = {};
  827.       for (var oldPref in oldPrefs) {
  828.         var prefName = oldPrefs[oldPref] ,prefValue = null;
  829.         if (tabxPrefs.prefHasUserValue(prefName)) {
  830.           switch (tabxPrefs.getPrefType(prefName)) {
  831.             case pBranch.PREF_BOOL:
  832.               prefValue = tabxPrefs.getBoolPref(prefName);
  833.               break;
  834.             case pBranch.PREF_INT:
  835.               var colorCodes = ["#CF1919", "#0E36EF", "#DDDF0D", "#3F8F3E", "#E066FF", "#86E7EF",
  836.                                  "#FFFFFF", "#7F7F7F", "#000000", "#EF952C", "#FF82AB", "#7F4C0F", "#AAAAFF"];
  837.               var _value = tabxPrefs.getIntPref(prefName);
  838.               if (_value >= 0 && _value < 13)
  839.                 prefValue = colorCodes[_value];
  840.               break;
  841.             case pBranch.PREF_STRING:
  842.               prefValue = tabxPrefs.getCharPref(prefName);
  843.               break;
  844.           }
  845.           tabxPrefs.clearUserPref(prefName);
  846.           if (prefValue != null) {
  847.             needToUpdatePref = true;
  848.             if (rule == "progressMeter")
  849.               oldPref = oldPref.replace("text", "bg");
  850.             prefsToChange[oldPref.replace("OLD", "")] = prefValue;
  851.           }
  852.         }
  853.       }
  854.       gPreventUpdate = false;
  855.       if (needToUpdatePref == true) {
  856.         this.converOldStylePrefs(rule, prefsToChange);
  857.       }
  858.     }
  859.  
  860.     // from Firefox 3 tab-icon-image class have -moz-margin-start: value; -margin-end-value: value;
  861.     // we apply these value dynamically here to our tab-protect-icon tab-lock-icon class since each theme use different values
  862.     var icon = document.getAnonymousElementByAttribute(gBrowser.mCurrentTab, "class", "tab-icon-image");
  863.     if (icon) {
  864.       var marginStart = window.getComputedStyle(icon, null).marginLeft;
  865.       var marginEnd = window.getComputedStyle(icon, null).marginRight;
  866.       var iconRule = ".tabbrowser-tab > .tab-image-middle > .tab-icon > .tab-protect-icon," +
  867.                       ".tabbrowser-tab > .tab-image-middle > .tab-icon > .tab-lock-icon {" +
  868.                        "-moz-margin-start: %S; -moz-margin-end: %S;}".replace("%S", marginStart).replace("%S", marginEnd);
  869.       ss.insertRule(iconRule, ss.cssRules.length);
  870.     }
  871.  
  872.     try {
  873.       this.replaceThrobberRules();
  874.     } catch (ex) {TMP_ASSERT(ex);}
  875.   },
  876.  
  877.   // update throbber for tab. for firefox 3.0.x - 3.6 there is one rule
  878.   // starting from 3.7+ - 2007-09-30 there are 10 rules
  879.   replaceThrobberRules: function TMP_PO_replaceThrobberRules() {
  880.     let browserCss;
  881.     for (let i = 0; i < document.styleSheets.length; ++i) {
  882.       if (document.styleSheets[i].href == "chrome://browser/skin/browser.css") {
  883.         browserCss = document.styleSheets[i];
  884.         break;
  885.       }
  886.     }
  887.     if (browserCss) {
  888.       let rulesCount = browserCss.cssRules.length;
  889.       let oldImage = "> .tab-icon-image";
  890.       let oldImageRe = new RegExp(oldImage);
  891.       let newImage = "> .tab-image-middle > .tab-icon > .tab-icon-image";
  892.       let newImageRe = new RegExp(newImage);
  893.  
  894.       let oldText = "> .tab-text";
  895.       let oldTextRe = new RegExp(oldText);
  896.       let newText = "> .tab-image-middle > .tab-text-stack > .tab-text";
  897.       let newTextRe = new RegExp(newText);
  898.       
  899.       for (let i = 0; i < rulesCount; ++i) {
  900.         let rule = browserCss.cssRules[i];
  901.         let selector = rule.selectorText;
  902.         if (oldImageRe.test(selector) && !newImageRe.test(selector)) {
  903.           let cssText = rule.cssText.replace(oldImage, newImage);
  904.           this.tabStyleSheet.insertRule(cssText, this.tabStyleSheet.cssRules.length);
  905.         }
  906.         else if (oldTextRe.test(selector) && !newTextRe.test(selector)) {
  907.           let cssText = rule.cssText.replace(oldText, newText);
  908.           this.tabStyleSheet.insertRule(cssText, this.tabStyleSheet.cssRules.length);
  909.         }
  910.       }
  911.     }
  912.     else {
  913.       tmLog('unable to finde "chrome://browser/skin/browser.css"');
  914.     }
  915.     delete this.tabStyleSheet;
  916.   },
  917.  
  918.   converOldStylePrefs: function TMP_PO_converOldStylePrefs(prefName, oldPrefs) {
  919.     var prefString = tabxPrefs.getCharPref("styles." + prefName);
  920.     try {
  921.       prefValues = Components.utils.evalInSandbox("({" + prefString  + "})" , new Components.utils.Sandbox("about:blank"));
  922.     } catch (ex) { return; } // nothing we can do
  923.     for (var item in oldPrefs)
  924.       prefValues[item] = oldPrefs[item];
  925.     var newprefValues = [];
  926.     for (item in prefValues) {
  927.       var comma = typeof(prefValues[item]) == "string" ? "'" : "";
  928.       newprefValues.push(item + ":" + comma + prefValues[item] + comma);
  929.     }
  930.     var newprefString = newprefValues.join(",");
  931.     if (newprefString != prefString)
  932.       tabxPrefs.setCharPref("styles." + prefName, newprefString);
  933.   },
  934.  
  935.   defaultStylePrefs: {    currentTab: "italic:false,bold:false,underline:false,text:true,textColor:'rgba(0,0,0,1)',bg:false,bgColor:'rgba(236,233,216,1)'",
  936.                            unreadTab: "italic:true,bold:false,underline:false,text:true,textColor:'rgba(204,0,0,1)',bg:false,bgColor:'rgba(236,233,216,1)'",
  937.                             otherTab: "italic:false,bold:false,underline:false,text:true,textColor:'rgba(0,0,0,1)',bg:false,bgColor:'rgba(236,233,216,1)'",
  938.                        progressMeter: "bg:true,bgColor:'rgba(170,170,255,1)'"},
  939.   tabStylePrefs: {},
  940.   setTabStyles: function TMP_PO_setTabStyles(prefName, start) {
  941.     var ruleName = prefName.split(".").pop();
  942.     if (ruleName in this && this[ruleName] == "preventUpdate")
  943.       return;
  944.     this[ruleName] = "preventUpdate";
  945.  
  946.     // Converts a color string in the format "#RRGGBB" to rgba(r,g,b,a).
  947.     function getRGBcolor(aColorCode, aOpacity) {
  948.       var newRGB = [];
  949.       var _length = aColorCode.length;
  950.       if (/^rgba|rgb/.test(aColorCode)) {
  951.         newRGB = aColorCode.replace(/rgba|rgb|\(|\)/g,"").split(",").splice(0, 4);
  952.         if (newRGB.length < 3)
  953.           return null;
  954.         for (var i = 0; i < newRGB.length; i++) {
  955.           if (isNaN(newRGB[i].replace(/[\s]/g,"") * 1))
  956.             return null ;
  957.         }
  958.       }
  959.       else if (/^#/.test(aColorCode) && _length == 4 || _length == 7) {
  960.         aColorCode = aColorCode.replace("#","");
  961.         var subLength = _length == 7 ? 2 : 1;
  962.         var newRGB = [];
  963.         for (var i = 0; i < 3; i++) {
  964.           var subS = aColorCode.substr(i*subLength, subLength);
  965.           if (_length == 4)
  966.             subS += subS;
  967.           var newNumber = parseInt(subS, 16);
  968.           if (isNaN(newNumber))
  969.             return null;
  970.           newRGB.push(newNumber);
  971.         }
  972.       }
  973.       else
  974.         return null;
  975.  
  976.       var opacity = newRGB[3];
  977.       if (aOpacity != null || opacity == null || opacity < 0 || opacity > 1)
  978.         newRGB[3] = aOpacity || 1;
  979.       return "rgba(" + newRGB.join(",") + ")";
  980.     }
  981.  
  982.     // styles format: italic:boolean, bold:boolean, underline:boolean,
  983.     //                text:boolean, textColor:string, textOpacity:string,
  984.     //                bg:boolean, bgColor:string, bgOpacity:striung
  985.     // if we don't catch the problem here it can break the rest of tabmix startup
  986.     try {
  987.       var defaultPrefValues = Components.utils.evalInSandbox("({" + this.defaultStylePrefs[ruleName]  + "})" , new Components.utils.Sandbox("about:blank"));
  988.     } catch (ex) {
  989.       this.tabStylePrefs[ruleName] = null;
  990.       tmLog('Error in preference "' + prefName + '", tabmix was unable to set style');
  991.       TMP_ASSERT(ex);
  992.       return;
  993.     }
  994.     var prefString = gTabmixPrefs.getCharPref(prefName);
  995.     var prefValues = {};
  996.     if (gTabmixPrefs.prefHasUserValue(prefName)) {
  997.       var _prefString = prefString.split(",");
  998.       for (var i = 0; i < _prefString.length; i++) {
  999.         if (_prefString[i].indexOf(":") == -1 || _prefString[i].indexOf("Color") > -1
  1000.              || _prefString[i].indexOf("Opacity") > -1)
  1001.           continue;
  1002.  
  1003.         var item = _prefString[i].split(":");
  1004.         if (/^true$|^false$/.test(item[1].replace(/[\s]/g,"")))
  1005.           continue;
  1006.       _prefString[i] = item[0] + ":" + null;
  1007.       }
  1008.       prefString = _prefString.join(",");
  1009.  
  1010.       try {
  1011.         var currentPrefValues = Components.utils.evalInSandbox("({" + prefString  + "})" , new Components.utils.Sandbox("about:blank"));
  1012.       }
  1013.       catch (ex) {
  1014.         tmLog('Error in preference "' + prefName + '", value was reset to default');
  1015.         TMP_ASSERT(ex);        
  1016.         if (gTabmixPrefs.prefHasUserValue(prefName))
  1017.           gTabmixPrefs.clearUserPref(prefName);
  1018.         // set prev value to default so we can continue with this function
  1019.         currentPrefValues = defaultPrefValues;
  1020.       }
  1021.  
  1022.       // make sure we have all the item
  1023.       // if item is missing set it to default
  1024.       var newprefValues = [];
  1025.       for (var item in defaultPrefValues) {
  1026.         var _value = currentPrefValues[item];
  1027.         if (item.indexOf("Color") > -1) {
  1028.          var opacity = item.replace("Color", "Opacity");
  1029.          var opacityValue = opacity in currentPrefValues ? currentPrefValues[opacity] : null;
  1030.           _value = getRGBcolor(_value, opacityValue);
  1031.         }
  1032.         else if (_value != null && typeof(_value) != "boolean") {
  1033.           if (/^true$|^false$/.test(_value.replace(/[\s]/g,"")))
  1034.             _value = _value == "true" ? true : false;
  1035.           else
  1036.             _value = null;
  1037.         }
  1038.         if (_value == null)
  1039.           prefValues[item] = defaultPrefValues[item];
  1040.         else
  1041.           prefValues[item] = _value;
  1042.         var comma = typeof(prefValues[item]) == "string" ? "'" : "";
  1043.         newprefValues.push(item + ":" + comma + prefValues[item] + comma);
  1044.       }
  1045.       var newprefString = newprefValues.join(",");
  1046.       if (newprefString != prefString)
  1047.         gTabmixPrefs.setCharPref(prefName, newprefString);
  1048.     }
  1049.     else
  1050.       prefValues = defaultPrefValues;
  1051.  
  1052.     // we don't change attribute to stay compatible with theme that maybe use this values
  1053.     var attrib = (ruleName.charAt(0).toUpperCase() + ruleName.substr(1)).replace(/Tab|Meter/,"");
  1054.     var tabBar = gBrowser.mTabContainer;
  1055.     if (ruleName !=  "progressMeter") {
  1056.       TMP_setItem(tabBar, "bold" + attrib, prefValues.bold);
  1057.       TMP_setItem(tabBar, "italic" + attrib, prefValues.italic);
  1058.       TMP_setItem(tabBar, "underline" + attrib, prefValues.underline);
  1059.       TMP_setItem(tabBar, "use"+ attrib + "Color", prefValues.text);
  1060.       TMP_setItem(tabBar, "use"+ attrib + "BGColor", prefValues.bg);
  1061.     }
  1062.     else
  1063.       TMP_setItem(tabBar, "use"+ attrib + "Color", prefValues.bg);
  1064.  
  1065.     var currentValue = this.tabStylePrefs[ruleName];
  1066.     if (currentValue && !start) {
  1067.       // we get here only when user changed pref value
  1068.       if (currentValue.bgColor != prefValues.bgColor)
  1069.         this.colorRules[ruleName + "bg"].style.setProperty("background-color",prefValues.bgColor, "important");
  1070.  
  1071.       if (ruleName !=  "progressMeter") {
  1072.         if (currentValue.textColor != prefValues.textColor)
  1073.           this.colorRules[ruleName].style.setProperty("color",prefValues.textColor, null);
  1074.  
  1075.         // changeing bold attribute can change tab width and effect tabBar scroll status
  1076.         if (currentValue.bold != prefValues.bold) {
  1077.           tabBarScrollStatus();
  1078.           checkBeforeAndAfter();
  1079.         }
  1080.       }
  1081.     }
  1082.     this.tabStylePrefs[ruleName] = prefValues;
  1083.     delete this[ruleName];
  1084.   },
  1085.  
  1086.   toggleTabStyles: function(prefName) {
  1087.     var ruleName = prefName.split(".").pop();
  1088.     gBrowser.mTabContainer.setAttribute(ruleName, tabxPrefs.getBoolPref(ruleName));
  1089.     var attrib = (ruleName.charAt(0).toUpperCase() + ruleName.substr(1)).replace("Tab","");
  1090.     var boldStyle = gBrowser.mTabContainer.getAttribute(attrib);
  1091.     if (boldStyle == "true") {
  1092.       tabBarScrollStatus();
  1093.       checkBeforeAndAfter();
  1094.     }
  1095.   },
  1096.  
  1097.   setLink_openPrefs: function() {
  1098.     if (!gSingleWindowMode)
  1099.       return;
  1100.  
  1101.     function updateStatus(pref, testVal, test, newVal) {
  1102.       try {
  1103.         var prefValue = gTabmixPrefs.getIntPref(pref);
  1104.         test = test ? prefValue == testVal : prefValue != testVal
  1105.       }
  1106.       catch(e){ test = true; }
  1107.  
  1108.       if (test)
  1109.         gTabmixPrefs.setIntPref(pref, newVal);
  1110.     }
  1111.  
  1112.     updateStatus("browser.link.open_external", 2, true, 3);
  1113.     updateStatus("browser.link.open_newwindow.restriction", 0, false, 0);
  1114.     updateStatus("browser.link.open_newwindow", 2, true, 3);
  1115.   },
  1116.  
  1117.   // code for Single Window Mode...
  1118.   // disable the "Open New Window action
  1119.   //disable & hides some menuitem
  1120.   setSingleWindowUI: function() {
  1121.     gSingleWindowMode = TMP_getBoolPref(tabxBranch, "singleWindow", false);
  1122.     var newWindowButton = document.getElementById("new-window-button");
  1123.     if (newWindowButton)
  1124.       newWindowButton.setAttribute("disabled", gSingleWindowMode);
  1125.  
  1126.     var openLink = document.getElementById("context-openlink");
  1127.     if (openLink)
  1128.       openLink.setAttribute("disabled", gSingleWindowMode);
  1129.  
  1130.     var menuItem;
  1131.     var menuFile = document.getElementById("menu_FilePopup");
  1132.     if (menuFile) {
  1133.       menuItem = menuFile.getElementsByAttribute("command", "cmd_newNavigator")[0];
  1134.       if (menuItem)
  1135.         menuItem.setAttribute("hidden", gSingleWindowMode);
  1136.     }
  1137.  
  1138.     var frameMenu = document.getElementById("frame");
  1139.     if (frameMenu) {
  1140.       menuItem = frameMenu.getElementsByAttribute("oncommand", "gContextMenu.openFrame();")[0];
  1141.       if (menuItem)
  1142.         menuItem.setAttribute("hidden", gSingleWindowMode);
  1143.     }
  1144.  
  1145.     document.getElementById("tmOpenInNewWindow").hidden = gSingleWindowMode;
  1146.   },
  1147.  
  1148.   setMenuIcons: function() {
  1149.     function setClass(items, hideIcons) {
  1150.       if (hideIcons)
  1151.         for (var i = 0; i < items.length; ++i)
  1152.           items[i].removeAttribute("class");
  1153.       else
  1154.         for ( i = 0; i < items.length; ++i)
  1155.           items[i].setAttribute("class", items[i].getAttribute("tmp_iconic"));
  1156.     }
  1157.     var hideIcons = TMP_getBoolPref(tabxBranch, "hideIcons", false);
  1158.     var iconicItems = document.getElementsByAttribute("tmp_iconic", "*");
  1159.     setClass(iconicItems, hideIcons);
  1160.  
  1161.     iconicItems = gBrowser.tabContextMenu.getElementsByAttribute("tmp_iconic", "*");
  1162.     setClass(iconicItems, hideIcons);
  1163.   },
  1164.  
  1165.   setAutoHidePref: function() {
  1166.     gHideTabBar = tabxPrefs.getIntPref("hideTabbar");
  1167.     var autoHide = gHideTabBar != 0;
  1168.     if (autoHide != gTabmixPrefs.getBoolPref("browser.tabs.autoHide"))
  1169.       gTabmixPrefs.setBoolPref("browser.tabs.autoHide", autoHide);
  1170.   },
  1171.  
  1172.   setTabBarVisibility: function TMP_PO_setTabBarVisibility(onFullScreenExit) {
  1173.     if (gHideTabBar == 2) {
  1174.       TMP_setStripVisibilityTo(false);
  1175.     }
  1176.     else if (!gBrowser.getStripVisibility() || onFullScreenExit) {
  1177.       // if we are in multi-row and we have more then one tab we need to initialize multi-row again
  1178.       gBrowser.mTabContainer.collapsedTabs = 0;
  1179.       setTabBarHeight(1);
  1180.       gBrowser.mTabContainer.removeAttribute("multibar");
  1181.       var moreThenOneTab = gBrowser.mTabContainer.childNodes.length > 1;
  1182.       if (onFullScreenExit) // from firefox 3.0 tab strip is collapsed by set moz-collapsed to true on full screen
  1183.         gBrowser.mStrip.setAttribute("moz-collapsed", false);
  1184.       else if (moreThenOneTab || gHideTabBar == 0)
  1185.         TMP_setStripVisibilityTo(true);
  1186.       if (moreThenOneTab) {
  1187.          tabBarScrollStatus();
  1188.          gBrowser.mTabContainer.ensureTabIsVisible(gBrowser.mTabContainer.selectedIndex);
  1189.          checkBeforeAndAfter();
  1190.          //XXX underline the label bleed over the end when tab is to small and we have close button on the tab !!!!????
  1191.          toggleUnderlineTabsLabel();
  1192.       }
  1193.     }
  1194.   },
  1195.  
  1196.   changeNewTabButtonSide: function(aPosition) {
  1197.     var tabBar = gBrowser.mTabContainer;
  1198.     tabBar._checkNewtabButtonVisibility = gIsFirefox35 && tabxPrefs.getBoolPref("newTabButton") && aPosition == 2;
  1199.     var newTabButton = document.getAnonymousElementByAttribute(tabBar, "id", "tabs-newbutton-box");
  1200.     // if we don't find tabs-newbutton-box prbebly some theme changed our binding
  1201.     if (!newTabButton)
  1202.       return;
  1203.     tabBar._rightNewTabButton = newTabButton;
  1204.  
  1205.     var buttonOnLeftSide = aPosition == 0;
  1206.     if (buttonOnLeftSide) {
  1207.       tabBar.setAttribute("newtab_side", "left");
  1208.       var leftContainer = document.getAnonymousElementByAttribute(tabBar, "class", "tabs-newTab");
  1209.       if (leftContainer)
  1210.         leftContainer.appendChild(newTabButton);
  1211.     }
  1212.     else {
  1213.       tabBar.setAttribute("newtab_side", aPosition == 1 ? "right" : "afterlast");
  1214.       var allTabsButton = document.getAnonymousElementByAttribute(tabBar, "class", "tabs-alltabs-stack");
  1215.       // if we don't find allTabsButton prbebly some them changed our binding
  1216.       // look at Vista-aero 2.0.0.46 for example.
  1217.       if (allTabsButton)
  1218.         allTabsButton.parentNode.insertBefore(newTabButton, allTabsButton);
  1219.     }
  1220.   },
  1221.  
  1222.   tabBarPositionChanged: function(aPosition) {
  1223.     if (aPosition > 1 || (aPosition != 0 && "TreeStyleTabBrowser" in window)) {
  1224.       tabxPrefs.setIntPref("tabBarPosition", 0);
  1225.       return false;
  1226.     }
  1227.     if (gTabbarPosition == aPosition)
  1228.       return false;
  1229.  
  1230.     gTabbarPosition = aPosition;
  1231.     gBrowser.mTabDropIndicatorBar.firstChild.removeAttribute("style");
  1232.     var tabsToolbar = document.getElementById("tabs-toolbar");
  1233.     if (gisToolbarMode && tabsToolbar) {
  1234.       if (gTabbarPosition == 1) {// bottom
  1235.         var bottomToolbox = document.getElementById("bottom-toolbox");
  1236.         if (!bottomToolbox) {
  1237.           bottomToolbox = document.createElement("toolbox");
  1238.           bottomToolbox.setAttribute("id", "bottom-toolbox");
  1239.           browser = document.getElementById("browser");
  1240.           browser.parentNode.insertBefore(bottomToolbox, browser.nextSibling);
  1241.         }
  1242.         bottomToolbox.appendChild(tabsToolbar);
  1243.       }
  1244.       else {// top
  1245.         // to prevent the toolbar from rebuild we remove currentset and defaultset attribute
  1246.         // befro we move the toolbar back to navigator-toolbox
  1247.         var currentSet = tabsToolbar.getAttribute("currentset");
  1248.         tabsToolbar.removeAttribute("currentset");
  1249.         var defaultSet = tabsToolbar.getAttribute("defaultset");
  1250.         tabsToolbar.removeAttribute("defaultset");
  1251.         document.getElementById("navigator-toolbox").appendChild(tabsToolbar);
  1252.         if (currentSet)
  1253.           tabsToolbar.setAttribute("currentset", currentSet);
  1254.         if (defaultSet)
  1255.           tabsToolbar.setAttribute("defaultset", defaultSet);
  1256.       }
  1257.     }
  1258.     return true;
  1259.   },
  1260.  
  1261.   // Show Reload Every menu on Reload button
  1262.   showReloadEveryOnReloadButton: function() {
  1263.     let reloadButton = document.getElementById("reload-button");
  1264.     if (!reloadButton)
  1265.       return;
  1266.  
  1267.     let show = gTabmixPrefs.getBoolPref("extensions.tabmix.reloadEvery.onReloadButton");
  1268.     let popup = document.getElementById("autoreload_popup_reloadButton");
  1269.     if (show && !popup) {
  1270.       let clonePopup = document.getElementById("autoreload_popup").cloneNode(true);
  1271.       clonePopup.setAttribute("id", "autoreload_popup_reloadButton");
  1272.       clonePopup.setAttribute("position", "after_start");
  1273.       reloadButton.appendChild(clonePopup);
  1274.     }
  1275.     else if (!show && popup)
  1276.       popup.parentNode.removeChild(popup);
  1277.  
  1278.     TMP_setItem(reloadButton, "type", show ? "menu-button" : null);
  1279.   },
  1280.  
  1281.   // we replace some Tabmix settings with Firefox settings
  1282.   updateSettings: function() {
  1283.     gPreventUpdate = true;
  1284.     if (tabxPrefs.prefHasUserValue("undoCloseCache")) {
  1285.        var max_tabs_undo = tabxPrefs.getIntPref("undoCloseCache");
  1286.        tabxPrefs.clearUserPref("undoCloseCache");
  1287.        gTabmixPrefs.setIntPref("browser.sessionstore.max_tabs_undo", max_tabs_undo);
  1288.     }
  1289.     // remove disp=attd&view=att it's make problem with gMail
  1290.     if (tabxPrefs.prefHasUserValue("filetype")) {
  1291.        var filetype = tabxPrefs.getCharPref("filetype");
  1292.        filetype = filetype.replace("/disp=attd&view=att/","").replace("  ", " ").replace(/^\s+|\s+$/g, "");
  1293.        tabxPrefs.setCharPref("filetype", filetype);
  1294.     }
  1295.     // 2008-08-17
  1296.     if (tabxPrefs.prefHasUserValue("opentabfor.search")) {
  1297.        gTabmixPrefs.setBoolPref("browser.search.openintab", tabxPrefs.getBoolPref("opentabfor.search"));
  1298.        tabxPrefs.clearUserPref("opentabfor.search");
  1299.     }
  1300.     // 2008-09-23
  1301.     if (tabxPrefs.prefHasUserValue("keepWindow")) {
  1302.        gTabmixPrefs.setBoolPref("browser.tabs.closeWindowWithLastTab", !tabxPrefs.getBoolPref("keepWindow"));
  1303.        tabxPrefs.clearUserPref("keepWindow");
  1304.     }
  1305.     // 2008-09-23
  1306.     if (gTabmixPrefs.prefHasUserValue("browser.ctrlTab.mostRecentlyUsed")) {
  1307.        gTabmixPrefs.setBoolPref("browser.ctrlTab.previews", gTabmixPrefs.getBoolPref("browser.ctrlTab.mostRecentlyUsed"));
  1308.        gTabmixPrefs.clearUserPref("browser.ctrlTab.mostRecentlyUsed");
  1309.     }
  1310.     // 2008-09-28
  1311.     if (tabxPrefs.prefHasUserValue("lasttab.handleCtrlTab")) {
  1312.        gTabmixPrefs.setBoolPref("browser.ctrlTab.previews", tabxPrefs.getBoolPref("lasttab.handleCtrlTab"));
  1313.        tabxPrefs.clearUserPref("lasttab.handleCtrlTab");
  1314.     }
  1315.     // 2008-11-29
  1316.     if (tabxPrefs.prefHasUserValue("maxWidth")) {
  1317.        gTabmixPrefs.setIntPref("browser.tabs.tabMaxWidth", tabxPrefs.getIntPref("maxWidth"));
  1318.        tabxPrefs.clearUserPref("maxWidth");
  1319.     }
  1320.     // 2008-11-29
  1321.     if (tabxPrefs.prefHasUserValue("minWidth")) {
  1322.        gTabmixPrefs.setIntPref("browser.tabs.tabMinWidth", tabxPrefs.getIntPref("minWidth"));
  1323.        tabxPrefs.clearUserPref("minWidth");
  1324.     }
  1325.     // 2009-01-31
  1326.     if (tabxPrefs.prefHasUserValue("newTabButton.leftside")) {
  1327.        tabxPrefs.setIntPref("newTabButton.position", tabxPrefs.getBoolPref("newTabButton.leftside") ? 0 : 2);
  1328.        tabxPrefs.clearUserPref("newTabButton.leftside");
  1329.     }
  1330.     // 2009-10-10
  1331.     // swap prefs --> warn when closing window "extensions.tabmix.windows.warnOnClose" replaced with "browser.tabs.warnOnClose"
  1332.     //                warn when closing tabs "browser.tabs.warnOnClose" replaced with "extensions.tabmix.tabs.warnOnClose"
  1333.     if (tabxPrefs.prefHasUserValue("windows.warnOnClose")) {
  1334.        tabxPrefs.setBoolPref("tabs.warnOnClose", gTabmixPrefs.getBoolPref("browser.tabs.warnOnClose"));
  1335.        gTabmixPrefs.setBoolPref("browser.tabs.warnOnClose", tabxPrefs.getBoolPref("windows.warnOnClose"));
  1336.        tabxPrefs.clearUserPref("windows.warnOnClose");
  1337.     }    
  1338.  
  1339.     // verify that all the prefs exist .....
  1340.     this.addMissingPrefs();
  1341.     gPreventUpdate = false;
  1342.   },
  1343.  
  1344.   // we call this function also from pref-tabmix.js
  1345.   addMissingPrefs: function() {
  1346.     const pBranch = Components.interfaces.nsIPrefBranch;
  1347.     function _setPref(aType, aPref, aDefault) {
  1348.       if (gTabmixPrefs.prefHasUserValue(aPref)) {
  1349.         if (gTabmixPrefs.getPrefType(aPref) == aType)
  1350.           return;
  1351.         else
  1352.           gTabmixPrefs.clearUserPref(aPref);
  1353.       }
  1354.       switch (aType) {
  1355.         case pBranch.PREF_BOOL:
  1356.           return TMP_getBoolPref("", aPref, aDefault);
  1357.         case pBranch.PREF_INT:
  1358.           return TMP_getIntPref("", aPref, aDefault);
  1359.         case pBranch.PREF_STRING:
  1360.           return TMP_getCharPref("", aPref, aDefault);
  1361.       }
  1362.     }
  1363.     _setPref(pBranch.PREF_BOOL, "browser.tabs.closeWindowWithLastTab", false); // exist in firefox version 3.5
  1364.     _setPref(pBranch.PREF_BOOL, "browser.ctrlTab.previews", true);     // exist in firefox version 3.5
  1365.     _setPref(pBranch.PREF_INT, "browser.link.open_external", 3);               // not exist in firefox from version 3.5
  1366.   }
  1367.  
  1368. }
  1369.  
  1370. // adjust attribute when we close 2nd tab (we left with only one) or when we open 2nd tab
  1371. var adjustOn2ndTab = {
  1372.   /*  don't show close tab button if there is only one blank tab or we keep last tab
  1373.    *  or if tab width is smaller then pref and not in WidthFitTitle mode.
  1374.    */
  1375.    closeButton: function adjust_closeButton(aNumTabs, oldTab) {
  1376.       var tabBar = gBrowser.mTabContainer;
  1377.       var tabs = tabBar.childNodes;
  1378.       var aTab = aNumTabs == 2 && tabs[0] == oldTab && tabs.length > 1 ? tabs[1] : tabs[0];
  1379.       if (tabs.length == aNumTabs &&
  1380.                (TMP_getBoolPref(tabxBranch, "keepLastTab", false) || gBrowser.isBlankNotBusyTab(aTab)))
  1381.          tabBar.setAttribute("hidebutton", "true");
  1382.         // from Firefox 3.5+ tabBar._removingTabs holds tab that are about to remove
  1383.         // when we add new tab before removeing the last one don't remove hidebutton attribute
  1384.       else if (tabBar.hasAttribute("hidebutton") &&
  1385.                (!gIsFirefox35 || tabs.length - gBrowser._removingTabs.length > 1))
  1386.         tabBar.removeAttribute("hidebutton");
  1387.  
  1388.       setTimeout( function(tabBar) {
  1389.          // make sure not to check collapsed tab for width
  1390.          var ltr = window.getComputedStyle(gBrowser, null).direction == "ltr";
  1391.          var width = ltr ? tabBar.lastChild.boxObject.width : tabBar.firstChild.boxObject.width;
  1392.          // 0 width is an invalid value and indicates an item without display,
  1393.          // so ignore.
  1394.          var tabClipWidth = TMP_getIntPref("", "browser.tabs.tabClipWidth", 140);
  1395.          var attributeChanged = false;
  1396.          if (addtabx != 5 || gWidthFitTitle || width > tabClipWidth || width == 0) {
  1397.             if (tabBar.hasAttribute("tinywidth")) {
  1398.                tabBar.removeAttribute("tinywidth");
  1399.                attributeChanged = true;
  1400.             }
  1401.          }
  1402.          else if (!tabBar.hasAttribute("tinywidth")) {
  1403.             tabBar.setAttribute("tinywidth", "true");
  1404.             attributeChanged = true;
  1405.          }
  1406.  
  1407.          if (attributeChanged) {
  1408.             tabBarScrollStatus();
  1409.             checkBeforeAndAfter();
  1410.          }
  1411.       }, 0, tabBar);
  1412.    }
  1413. }
  1414.  
  1415. function TMP_getBoolPref(branch, prefname, def ) {
  1416.   try {
  1417.     return gTabmixPrefs.getBoolPref(branch+prefname);
  1418.   }
  1419.   catch(er) {
  1420.     gTabmixPrefs.setBoolPref(branch+prefname, def);
  1421.     return def;
  1422.   }
  1423. }
  1424.  
  1425. function TMP_getIntPref(branch, prefname, def ) {
  1426.   try {
  1427.     return gTabmixPrefs.getIntPref(branch+prefname);
  1428.   }
  1429.   catch(er) {
  1430.     gTabmixPrefs.setIntPref(branch+prefname, def);
  1431.     return def;
  1432.   }
  1433. }
  1434.  
  1435. function TMP_getCharPref(branch, prefname, def ) {
  1436.   try {
  1437.     return gTabmixPrefs.getCharPref(branch+prefname);
  1438.   }
  1439.   catch(er) {
  1440.     gTabmixPrefs.setCharPref(branch+prefname, def);
  1441.     return def;
  1442.   }
  1443. }
  1444.  
  1445. var TMP_ProgressListener = {
  1446.  
  1447.    startup: function TMP_PL_startup(tabBrowser) {
  1448.       if (gIsFirefox35) {
  1449.         eval("tabBrowser.swapBrowsersAndCloseOther ="+tabBrowser.swapBrowsersAndCloseOther.toString().replace(
  1450.           'this.mTabListeners[ourIndex] = tabListener;',
  1451.           'tabListener = TMP_ProgressListener.init(tabListener); \ $&'
  1452.         ));
  1453.       }
  1454.  
  1455.       if (gIsFirefox35 && tabBrowser.mTabFilters[0]) {
  1456.         // remove exiasting event listeners
  1457.         tabBrowser.webProgress.removeProgressListener(tabBrowser.mTabFilters[0]);
  1458.         tabBrowser.mTabFilters[0].removeProgressListener(tabBrowser.mTabListeners[0]);
  1459.  
  1460.         // Hook up our event listeners to the first browser
  1461.         var tabListener = tabBrowser.mTabProgressListener(tabBrowser.selectedTab, tabBrowser.selectedBrowser, true);
  1462.         tabListener = this.init(tabListener);
  1463.         const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
  1464.                                  .createInstance(Components.interfaces.nsIWebProgress);
  1465.         filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1466.         tabBrowser.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  1467.         tabBrowser.mTabListeners[0] = tabListener;
  1468.         tabBrowser.mTabFilters[0] = filter;
  1469.       }
  1470.       else {
  1471.         eval("tabBrowser.enterTabbedMode ="+tabBrowser.enterTabbedMode.toString().replace(
  1472.           'filter.addProgressListener(listener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);',
  1473.           'listener = TMP_ProgressListener.init(listener); \ $&'
  1474.         ));
  1475.       }
  1476.  
  1477.       eval("tabBrowser.addTab ="+tabBrowser.addTab.toString().replace(
  1478.          'var tabListener = this.mTabProgressListener(t, b, blank);',
  1479.          '$& \ tabListener = TMP_ProgressListener.init(tabListener);'
  1480.       ));
  1481.    },
  1482.  
  1483.    init: function TMP_PL_init(progressListener) {
  1484.       var _this = "TMP_ProgressListener."
  1485.  
  1486.       function addToTheEnd(fnName, newString) {
  1487.          var fnString = progressListener[fnName].toString();
  1488.          fnString = fnString.substr(0, fnString.length - 2) + newString + "}";
  1489.          eval("progressListener." + fnName + " = " + fnString);
  1490.       }
  1491.  
  1492.       addToTheEnd('onProgressChange',
  1493.                   'this.onStateChange(aWebProgress, aRequest);' +
  1494.                   _this + 'setTabProgress(this.mTab, aMaxTotalProgress, aCurTotalProgress);');
  1495.  
  1496.       // we need to be compatible with XHTML Ruby Support
  1497.       var fnName = "onStateChange";
  1498.       var ruby = "__rubysupport__" + fnName;
  1499.       if (ruby in progressListener)
  1500.          fnName = ruby;
  1501.  
  1502.       eval("progressListener." + fnName + " = " + progressListener[fnName].toString().replace(
  1503.             'this.mTab.setAttribute("busy", "true");',
  1504.             _this + 'setTabWidth(this.mTab, aRequest.QueryInterface(nsIChannel).URI.spec);'
  1505.             + '$&'
  1506.          ).replace(
  1507.             'var location = aRequest.QueryInterface(nsIChannel).URI;',
  1508.             _this + 'setUnreadTab(this.mTab); \ $&'
  1509.          ));
  1510.  
  1511.       addToTheEnd(fnName,
  1512.                   _this + 'setAutoReload(this.mTab, this.mBrowser, aStateFlags);');
  1513.  
  1514.       // XXX how to do this with Regexp and replace ?
  1515.       var string = "if (this.mTabBrowser.mCurrentTab == this.mTab)";
  1516.       var split_fn = progressListener[fnName].toString().split(string);
  1517.       split_fn[1] += _this + 'updateSessionManager(this.mTab);'
  1518.       eval("progressListener." + fnName + " = " + split_fn.join(string));
  1519.  
  1520.       addToTheEnd('onLocationChange', _this + 'fixBug355253(this.mBrowser);')
  1521.  
  1522.       return progressListener;
  1523.    },
  1524.  
  1525.    setTabProgress: function TMP_PL_setTabProgress(aTab, aMaxTotalProgress, aCurTotalProgress) {
  1526.       if (gHideTabBar == 2 || aMaxTotalProgress < 1)
  1527.          return;
  1528.       var percentage = parseInt((aCurTotalProgress * 100) / aMaxTotalProgress);
  1529.       if (percentage > 0 && percentage < 100)
  1530.          aTab.setAttribute("tab-progress", percentage);
  1531.    },
  1532.  
  1533.    setTabWidth: function TMP_PL_setTabWidth(aTab, aLocation) {
  1534.       var tabBar = aTab.parentNode;
  1535.       var spacer = 0;
  1536.       // at this stage only unhide the button if needed.
  1537.       var buttonIsHidden = tabBar.hasAttribute("hidebutton");
  1538.       var keepLastTab = tabxPrefs.getBoolPref("keepLastTab");
  1539.       if (tabBar.childNodes.length == 1 && (buttonIsHidden || keepLastTab)) {
  1540.         var hidebutton = (aLocation == "about:blank") || keepLastTab;
  1541.         if (buttonIsHidden && !hidebutton)
  1542.           spacer = 5;
  1543.         TMP_setItem(tabBar, "hidebutton", hidebutton || null);
  1544.       }
  1545.       if (gHideTabBar != 2 && gWidthFitTitle && !aTab.hasAttribute("width") && aLocation != aTab.label) {
  1546.          if (aTab.hasAttribute("newtab")) {
  1547.            spacer = 5;
  1548.            aTab.removeAttribute("newtab");
  1549.          }
  1550. //XXX check if we need to use spacer
  1551.          aTab.setAttribute("width", aTab.boxObject.width);
  1552.       }
  1553.    },
  1554.  
  1555.    setUnreadTab: function TMP_PL_setUnreadTab(aTab) {
  1556.       if (aTab.parentNode.childNodes.length == 1) {
  1557.         var hidebutton = gBrowser.isBlankTab(aTab) || tabxPrefs.getBoolPref("keepLastTab");
  1558.         TMP_setItem(aTab.parentNode, "hidebutton", hidebutton || null);
  1559.       }
  1560.       aTab.removeAttribute("tab-progress");
  1561.       if (gTabmixPrefs.getBoolPref("extensions.tabmix.unreadTab") &&
  1562.             aTab.hasAttribute("visited") &&
  1563.             gTabmixPrefs.getBoolPref("extensions.tabmix.unreadTabreload") &&
  1564.             !aTab.hasAttribute("dontremovevisited") &&
  1565.             aTab.getAttribute("selected") != "true")
  1566.          aTab.removeAttribute("visited");
  1567.  
  1568.        // see gBrowser.openLinkWithHistory in tablib.js
  1569.        if (aTab.hasAttribute("dontremovevisited"))
  1570.           aTab.removeAttribute("dontremovevisited")
  1571.    },
  1572.  
  1573.    updateSessionManager: function TMP_PL_updateSessionManager(aTab) {
  1574.       if (!aTab.hasAttribute("busy"))
  1575.          SessionManager.tabLoaded(aTab);
  1576.    },
  1577.  
  1578.    setAutoReload: function TMP_PL_setAutoReload(aTab, aBrowser, aStateFlags) {
  1579.       const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  1580.       if ((aStateFlags & nsIWebProgressListener.STATE_IS_WINDOW) &&
  1581.             (aStateFlags & nsIWebProgressListener.STATE_STOP)) {
  1582.          if (aTab.autoReloadURI)
  1583.             gBrowser.onTabReloaded(aTab, aBrowser);
  1584.  
  1585.          // disabled name for locked tab, so locked tab don't get reuse
  1586.          if (aTab.getAttribute("locked") && aBrowser.contentWindow.name)
  1587.             aBrowser.contentWindow.name = "";
  1588.       }
  1589.    },
  1590.  
  1591. // XXX temp fix to bug 355253
  1592.    // recently closed tabs doesn't saved closed tab if it have "about:blank" first in history.
  1593.    fixBug355253: function TMP_PL_fixBug355253(aBrowser) {
  1594.       var history = aBrowser.webNavigation.sessionHistory;
  1595.       if (history && history.count == 2 && history.index == 1 &&
  1596.             history.getEntryAtIndex(0, false).URI.spec == "about:blank") {
  1597.          aBrowser.observe(null, "browser:purge-session-history", null);
  1598.          var backCommand = document.getElementById("Browser:Back");
  1599.          if (backCommand)
  1600.             backCommand.setAttribute("disabled", "true");
  1601.       }
  1602.    }
  1603.  
  1604. }
  1605.  
  1606. var TMP_TabGroupsManager = {
  1607.   init: function (tabBar) {
  1608.     // for firefox 3.0
  1609. //    var tabBar = gBrowser.mTabContainer;
  1610.  
  1611.     tabBar.__defineGetter__("lastTabVisible", function () {return TabGroupsManagerApiForTMPVer1.lastTabVisible;});
  1612.  
  1613.     // replace gBrowser.mTabContainer.lastChild with current group lastChild
  1614.     var selectedGroupLastChild = "TabGroupsManagerApiVer1.lastTab";
  1615.     eval("tabBar.adjustNewtabButtonvisibility ="+tabBar.adjustNewtabButtonvisibility.toString().replace(
  1616.       /this\.lastChild/g, selectedGroupLastChild
  1617.     ));
  1618.  
  1619.     var _getter = tabBar.__lookupGetter__("lastTabRowNumber");
  1620.     eval("_getter ="+_getter.toString().replace("this.lastChild", selectedGroupLastChild));
  1621.     tabBar.__defineGetter__("lastTabRowNumber", _getter);
  1622.  
  1623.     // replace gBrowser.mTabs or gBrowser.mTabContainer.childNodes with current group tabs
  1624.     var selectedGroupTabs = "TabGroupsManagerApiVer1.visibleTabs";
  1625.     eval("tabBar.adjustScrollTabsRight ="+tabBar.adjustScrollTabsRight.toString().replace(
  1626.       /this\.childNodes/g, selectedGroupTabs
  1627.     ));
  1628.  
  1629.     eval("tabBar.rowScroll ="+tabBar.rowScroll.toString().replace("this.childNodes", selectedGroupTabs));
  1630.  
  1631.     _getter = tabBar.__lookupGetter__("collapsedTabs");
  1632.     var _setter = tabBar.__lookupSetter__("collapsedTabs");
  1633.     tabBar.__defineGetter__("collapsedTabs", _getter);
  1634.     eval("_setter ="+_setter.toString().replace("this.childNodes", selectedGroupTabs));
  1635.     tabBar.__defineSetter__("collapsedTabs", _setter);
  1636.  
  1637.     tabBar.__defineGetter__("topTabY", this._getTopTabY);
  1638.  
  1639.     // only allow to show tabs from the current group
  1640.     eval("tabBar.isTabVisible="+tabBar.isTabVisible.toString().replace(
  1641.       'this.childNodes', selectedGroupTabs
  1642.     ).replace(
  1643.       '{',
  1644.       '{aIndex = TabGroupsManagerApiVer1.getIndexInSelectedGroupFrom_tPos(aIndex);'
  1645.     ));
  1646.  
  1647.     eval("tabBar.ensureTabIsVisible="+tabBar.ensureTabIsVisible.toString().replace(
  1648.       'this.childNodes', selectedGroupTabs
  1649.     ).replace(
  1650.       'const tabs',
  1651.       'aIndex = TabGroupsManagerApiVer1.getIndexInSelectedGroupFrom_tPos(aIndex); $&'
  1652.     ));
  1653.  
  1654.     eval("tabBar._notifyBackgroundTab="+tabBar._notifyBackgroundTab.toString().replace(
  1655.       'this.childNodes', selectedGroupTabs
  1656.     ).replace(
  1657.       'this.selectedIndex >= this.collapsedTabs',
  1658.       'TabGroupsManagerApiVer1.getIndexInSelectedGroupFrom_tPos(this.selectedIndex) >= this.collapsedTabs'
  1659.     ));
  1660.  
  1661.     // make scrool button show hidden tabs only from the current group
  1662.     eval("createCommonList="+createCommonList.toString().replace(
  1663.       'tabs = gBrowser.mTabs;',
  1664.       'tabs = side ? ' + selectedGroupTabs + ' : gBrowser.mTabs;'
  1665.     ));
  1666.  
  1667.     eval("TMP_Places.openGroup="+TMP_Places.openGroup.toString().replace("tabBar.childNodes", selectedGroupTabs));
  1668.  
  1669.     eval("toggleUnderlineTabsLabel="+toggleUnderlineTabsLabel.toString().replace('tabBar.childNodes', selectedGroupTabs));
  1670.  
  1671.     eval("TMP_eventListener.onTabOpen="+TMP_eventListener.onTabOpen.toString().replace(
  1672.       'this.onTabOpen_updateTabBar();',
  1673.       'try {if (TabGroupsManager.apiEnabled) TabGroupsManager.eventListener.onTabOpen(aEvent);} catch(e) { }'
  1674.     ));
  1675.  
  1676.     eval("TMP_eventListener.onTabClose="+TMP_eventListener.onTabClose.toString().replace(
  1677.       'this.onTabClose_updateTabBar(aTab);',
  1678.       'try {TabGroupsManager.eventListener.onTabClose(aEvent);} catch(e) { }'
  1679.     ));
  1680.  
  1681.     eval("TMP_eventListener.onTabClose_updateTabBar="+TMP_eventListener.onTabClose_updateTabBar.toString().replace(
  1682.       'aTab._tPos < tabBar.collapsedTabs',
  1683.       'TabGroupsManagerApiVer1.getIndexInSelectedGroupFromTab(aTab) < tabBar.collapsedTabs'
  1684.     ));
  1685.  
  1686.     eval("TabDNDObserver.onDragOver="+TabDNDObserver.onDragOver.toString().replace(
  1687.       'this.getNewIndex(event)',
  1688.       'TMP_TabGroupsManager._getDNDIndex(event)'
  1689.     ));
  1690.  
  1691.     eval("TabDNDObserver.onDrop="+TabDNDObserver.onDrop.toString().replace(
  1692.       'this.getNewIndex(event)',
  1693.       'TMP_TabGroupsManager._getDNDIndex(event)'
  1694.     ).replace(    
  1695.       'oldIndex < firstVisibleIndex',
  1696.       'TabGroupsManagerApiVer1.getIndexInSelectedGroupFrom_tPos(oldIndex) < firstVisibleIndex'
  1697.     ).replace(
  1698.       'gBrowser.mTabs[firstVisibleIndex - 1]',
  1699.       selectedGroupTabs + '[firstVisibleIndex - 1]'
  1700.     ));
  1701.  
  1702.     eval("TabDNDObserver.onDragEnd="+TabDNDObserver.onDragEnd.toString().replace('tabBar.childNodes', selectedGroupTabs));
  1703.  
  1704.     eval("TabDNDObserver.onDragExit="+TabDNDObserver.onDragExit.toString().replace(
  1705.       'if (target)',
  1706.       'if (target && !(/^TabGroupsManager/.test(target.id)))'
  1707.     ));
  1708.  
  1709.     eval("TabDNDObserver.getNewIndex="+TabDNDObserver.getNewIndex.toString().replace(
  1710.       'tabBar.childNodes', selectedGroupTabs
  1711.     ).replace(
  1712.       /event\.target\._tPos/g,
  1713.       'TabGroupsManagerApiVer1.getIndexInSelectedGroupFromTab(event.target)'
  1714.     ).replace(
  1715.       /isTabVisible\(i\)/g,
  1716.       'isTabVisible(tabs[i]._tPos)'
  1717.     ));
  1718.  
  1719.     eval("getRowHeight="+getRowHeight.toString().replace(
  1720.       'tabBar.childNodes', selectedGroupTabs
  1721.     ).replace(
  1722.       /tabBar\.lastChild/g, selectedGroupLastChild
  1723.     ));
  1724.  
  1725.     eval("tabBarWidthChange="+tabBarWidthChange.toString().replace(
  1726.       'tabBar.childNodes', selectedGroupTabs
  1727.     ).replace(
  1728.       'tabBar.ensureTabIsVisible(index);',
  1729.       'tabBar.ensureTabIsVisible(tabs[index]._tPos);'
  1730.     ).replace(
  1731.       'tabBar.lastChild', selectedGroupLastChild
  1732.     ));
  1733.  
  1734.     eval("adjustOn2ndTab.closeButton="+adjustOn2ndTab.closeButton.toString().replace(
  1735.       'tabBar.lastChild', selectedGroupLastChild
  1736.     ).replace(
  1737.       'tabBar.firstChild', 'TabGroupsManagerApiVer1.firstTab'
  1738.     ));
  1739.  
  1740.     // **************************** for Session Manager ****************************
  1741.     eval("SessionData.getTabProperties="+SessionData.getTabProperties.toString().replace(
  1742.       'return tabProperties;',
  1743.       'if (aTab.group && aTab.group.id) \
  1744.          tabProperties += " tabgroups-data=" + encodeURI(aTab.group.id + " " + aTab.group.name); \
  1745.        $&'
  1746.     ));
  1747.  
  1748.     eval("SessionManager.saveOneWindow="+SessionManager.saveOneWindow.toString().replace(
  1749.       'if (caller == "windowbackup")',
  1750.       <![CDATA[
  1751.         this.saveAllGroupsData(null, rdfNodeThisWindow);
  1752.         $&
  1753.       ]]>
  1754.     ));
  1755.  
  1756.     eval("SessionManager.loadOneWindow="+SessionManager.loadOneWindow.toString().replace(
  1757.       'var lastSelectedIndex = restoreSelect ? this.getIntValue(rdfNodeWindow, "selectedIndex") : 0;',
  1758.       '$&\
  1759.        var [_restoreSelect, _lastSelectedIndex] = [restoreSelect, lastSelectedIndex];\
  1760.        [restoreSelect, lastSelectedIndex] = [false, 0];'
  1761.     ).replace(
  1762.       'var selectedTabLoaded;',
  1763.       <![CDATA[
  1764.         try {
  1765.           let jsonText = this.getLiteralValue(rdfNodeWindow, "tgm_jsonText");
  1766.           if (jsonText)
  1767.             TMP_ClosedTabs.ss.setWindowValue(window, "TabGroupsManagerAllGroupsData", decodeURI(jsonText));
  1768.           TabGroupsManager.session.groupRestored = 0;
  1769.           TabGroupsManager.session.restoreGroupsAndSleepingGroupsAndClosedGroups();
  1770.         } catch (ex) {TMP_ASSERT(ex);}
  1771.         $&
  1772.       ]]>
  1773.     ).replace(
  1774.       'if (this.saveClosedtabs)',
  1775.       <![CDATA[
  1776.         if (_restoreSelect && (overwrite || (!concatenate && !currentTabIsBalnk)))
  1777.           this.updateSelected(newIndex + _lastSelectedIndex, overwrite || caller=="firstwindowopen" || caller=="windowopenebytabmix");
  1778.         $&
  1779.       ]]>
  1780.     ));
  1781.  
  1782.     eval("SessionManager.loadOneTab="+SessionManager.loadOneTab.toString().replace(
  1783.       'var savedHistory = this.loadTabHistory(rdfNodeSession, webNav.sessionHistory);',
  1784.       <![CDATA[
  1785.         $&
  1786.         try {
  1787.           var tabgroupsData = TMP_SessionStore._getAttribute({xultab: tabProperties}, "tabgroups-data");
  1788.           if (tabgroupsData) {
  1789.             let [groupId, groupName] = tabgroupsData.split(" ");
  1790.             TMP_ClosedTabs.ss.setTabValue(aTab, "TabGroupsManagerGroupId", groupId);
  1791.             TMP_ClosedTabs.ss.setTabValue(aTab, "TabGroupsManagerGroupName", groupName);
  1792.           }
  1793.           TabGroupsManager.session.moveTabToGroupBySessionStore(aTab);
  1794.         } catch (ex) {TMP_ASSERT(ex);}
  1795.       ]]>
  1796.     ));
  1797.  
  1798.     eval("SessionManager.setNC_TM="+SessionManager.setNC_TM.toString().replace(
  1799.       'for',
  1800.       'rdfLabels.push("tgm_jsonText");\
  1801.        $&'
  1802.     ));
  1803.  
  1804.     SessionManager.saveAllGroupsData = this._saveAllGroupsData;
  1805.   },
  1806.  
  1807.   _getTopTabY: function () {
  1808.     return this.tabstripInnerbox.boxObject.y + parseInt(window.getComputedStyle(this.tabstripInnerbox, null).paddingTop);
  1809.   },
  1810.   
  1811.   // get _tPos from group index
  1812.   _getDNDIndex: function (aEvent) {
  1813.     var indexInGroup = TabDNDObserver.getNewIndex(aEvent);
  1814.     var lastIndex = TabGroupsManagerApiVer1.visibleTabs.length - 1;
  1815.     if (indexInGroup < 0 || indexInGroup > lastIndex)
  1816.       indexInGroup = lastIndex;
  1817.     return TabGroupsManagerApiVer1.visibleTabs[indexInGroup]._tPos;
  1818.   },
  1819.  
  1820.   // for TabGroupsManager use
  1821.   tabmixSessionsManager: function () {
  1822.     return gTabmixPrefs.getBoolPref("extensions.tabmix.sessions.manager") &&
  1823.         (!TMP_SessionStore.isAfterRestart() || "tabmixdata" in window)
  1824.   },
  1825.  
  1826.   // for TabGroupsManager use
  1827.   _saveAllGroupsData: function (jsonText, windowNode) {
  1828.     if (!this.enableBackup && !windowNode)
  1829.       return;
  1830.     try {
  1831.       let value = jsonText || TMP_ClosedTabs.ss.getWindowValue(window, "TabGroupsManagerAllGroupsData");
  1832.       if (!windowNode)
  1833.         windowNode = gThisWin;
  1834.       this.setLiteral(windowNode, "tgm_jsonText", encodeURI(value));
  1835.       this.saveStateDelayed();
  1836.     } catch (ex) {
  1837.       TMP_ASSERT(ex);
  1838.     }
  1839.   }
  1840.  
  1841. }
  1842.